/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.server.game;

import com.core.log.Debug;
import com.core.utils.Utils;
import com.server.database.ChildInfo;
import com.server.database.DataEngine;
import com.server.database.MateInfo;
import com.server.database.UserInfo;
import com.server.main.ClientManager;
import java.util.List;
import java.util.Vector;

/**
 *
 * @author daz
 */
public abstract class GameTable {

    public static final int PHOM = 0;
    public static final int TIEN_LEN = 2;
    public static final int BA_CAY = 3;
    public static final int CHESS = 4;
    public static final int CHINESE_CHESS = 6;
    public static final int POKER = 7;
    public static final int XI_DACH = 8;
    public static final int CA_RO = 9;
    public static final String NONE_PASS = "?";
    public final float CHARGE = 0.9f;
    public static final int MAX_USERS = 20;
    protected GamePlayer[] listPlayer;
    protected Vector<String> listJoin = new Vector<String>();
    protected int numberUsers;
    protected int viewIndex;
    protected boolean isPlay;
    protected int tableHost;
    protected int theFirstTurn;
    protected int maxPlayers;    // So nguoi choi toi da co the duoc trong 1 game
    protected int maxPlayersInMatch; // Thiet lap so nguoi choi toi da trong van
    protected int numberReadyPlayres;
    protected long tableMoneyBet; // tiền cược
    private GameManager gameManager;
    private DataEngine dataEngine = DataEngine.getInstance();
    public int gameRule;
    public int userSendId;
    public int userReceiveId;
    protected int gameType;
    private String tablePassword = NONE_PASS;
    private GameRoom parent;
    private long moneyBetDefault;
    private int kindGame;

    public void setGameRule(int rule) {
        this.gameRule = rule;
    }

    public int getGameRule() {
        return this.gameRule;
    }

    public GameTable(int kindGame, int maxPlayers, GameManager gameManager, GameRoom parent, long moneyBetDefautl) {
        this.kindGame = kindGame;
        this.maxPlayers = maxPlayers;
        this.gameManager = gameManager;
        this.parent = parent;
        listPlayer = new GamePlayer[MAX_USERS];
        maxPlayersInMatch = maxPlayers;
        numberUsers = 0;
        isPlay = false;
        viewIndex = maxPlayers;
        this.moneyBetDefault = moneyBetDefautl;
        this.tableMoneyBet = moneyBetDefautl;
    }

    private void sendMaxPlayerInMatchUpdate() {
        String msg = "UUS";
        for (int i = 0; i < GameManager.NUMBER_TABLES_IN_ROOM; i++) {
            msg += "|" + parent.listTable[i].getMaxPlayersInMatch();
        }
        parent.sendToAllPlayerInRoom(msg);
    }

    private void sendTableBetUpdate() {
        String msg = "USM";
        for (int i = 0; i < GameManager.NUMBER_TABLES_IN_ROOM; i++) {
            msg += "|" + parent.listTable[i].getBet();
        }
        parent.sendToAllPlayerInRoom(msg);
    }

    private void sendNumberUserInRoomUpdate() {
        String msg = "USE";
        for (int i = 0; i < GameManager.NUMBER_TABLES_IN_ROOM; i++) {
            msg += "|" + parent.listTable[i].getNumberUsers();
        }
        parent.sendToAllPlayerInRoom(msg);
    }

    private void sendTablePassUpdate() {
        String msg = "UPAS";
        for (int i = 0; i < GameManager.NUMBER_TABLES_IN_ROOM; i++) {
            msg += "|" + parent.listTable[i].getTablePassword();
        }
        parent.sendToAllPlayerInRoom(msg);
    }

    private synchronized boolean addPlayer(GamePlayer player) {
        player.resetTimeDb();
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] == null) {
                listPlayer[i] = player;
                player.setTable(this, i);
                numberUsers++;
                return true;
            }
        }
        return false;
    }

    private synchronized boolean addView(GamePlayer player) {
        player.resetTimeDb();
        listPlayer[viewIndex] = player;
        player.setTable(this, viewIndex);
        viewIndex++;
        numberUsers++;
        return true;
    }

    private synchronized void removePlayer(int playerId) {
        listPlayer[playerId] = null;
        numberUsers--;
        if (numberUsers < 0) {
            numberUsers = 0;
        }

        if (numberUsers > 0) {
            boolean allPlayerQuit = true;
            for (int i = 0; i < maxPlayersInMatch; i++) {
                if (listPlayer[i] != null) {
                    allPlayerQuit = false;
                    break;
                }
            }
            if (allPlayerQuit) {
                if (listJoin.size() > 0) {
                    updatePlayer();
                    listJoin.removeAllElements();
                } else {
                    isPlay = false;
                    maxPlayersInMatch = maxPlayers;
                    tableMoneyBet = 0;
                    numberReadyPlayres = 0;
                    tablePassword = NONE_PASS;
                    tableHost = 0;
                    theFirstTurn = 0;
                    sendTablePassUpdate();
                    int count = 0;
                    for (int i = maxPlayers; i < viewIndex; i++) {
                        if (++count > maxPlayersInMatch) {
                            break;
                        }
                        listJoin.add(listPlayer[i].getUserName());
                    }
                    updatePlayer();
                    listJoin.removeAllElements();
                }
            }
        }
    }

    private synchronized void removeView(int playerId) {
        for (int i = playerId; i < viewIndex - 1; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i] = listPlayer[i + 1];
                listPlayer[i].setTable(this, i);
            }
        }
        listPlayer[viewIndex - 1] = null;
        viewIndex--;
        numberUsers--;
        if (numberUsers < 0) {
            numberUsers = 0;
        }
    }

    protected synchronized boolean addJoin(String userName) {
        if (listJoin.size() < (maxPlayersInMatch - getNumberActivedPlayers())) {
            listJoin.add(userName);
            return true;
        } else {
            return false;
        }
    }

    private void updatePlayer() {
        boolean stop = false, sendInfo = false;;
        while (!stop && listJoin.size() > 0) {
            String name = listJoin.elementAt(0);
            boolean match = false, addOK = false;
            int index = 0;
            GamePlayer player = null;
            for (int i = maxPlayers; i < viewIndex; i++) {
                if (listPlayer[i] != null && listPlayer[i].getUserName().equals(name)) {
                    index = i;
                    player = listPlayer[i];
                    match = true;
                    break;
                }
            }
            if (!match) {
                stop = true;
            }
            if (player != null && addPlayer(player)) {
                removeView(index);
                addOK = true;
                sendInfo = true;
            }
            if (addOK) {
                if (!listJoin.isEmpty()) {
                    listJoin.removeElementAt(0);
                }
            }
        }

        if (sendInfo) {
            sendTableInfo();
        }
    }

    public String getTablePassword() {
        return tablePassword;
    }

    public void sendRequestFriend(String sender, String receiver, String request) {
        if (dataEngine.checkFriend(sender, receiver)) {
            listPlayer[userSendId].response("RMF|2");
            return;
        }
        for (int i = 0, l = listPlayer.length; i < l; i++) {
            if (listPlayer[i] != null && listPlayer[i].getUserName().equals(receiver)) {
                listPlayer[i].response(request);
                userReceiveId = i;
                break;
            }
        }
//        listPlayer[userReceiveId].response(request);
    }

    private void sendTableInfo() {
        sendGameInfo();
        sendUserInfo();
        sendListPlayer();
        sendMoneyInfo();
        sendReadyState();
    }

    private void sendListView() {
        // Thong tin ban choi gui cho nguoi vao view
        String msg = "VIEW";
        for (int i = maxPlayers; i < viewIndex; i++) {
            if (listPlayer[i] != null) {
                msg += "|" + listPlayer[i].getUserName();
            }
        }
        if (msg.equals("VIEW")) {
            msg += "|";
        }
        sendToAll(msg);
    }

    private synchronized void removeJoin(String name) {
        for (int i = 0; i < listJoin.size(); i++) {
            if (listJoin.elementAt(i).equals(name)) {
                listJoin.removeElementAt(i);
                return;
            }
        }
    }

    public int getMaxPlayersInMatch() {
        return maxPlayersInMatch;
    }

    public int getNumberUsers() {
        return numberUsers;
    }

    public boolean getIsPlay() {
        return isPlay;
    }

    public long getBet() {
        return tableMoneyBet;
    }

    public void setBet(long bet) {
        this.tableMoneyBet = bet;
        sendTableBetUpdate();
    }

    public int getNumberActivedPlayers() {
        int number = 0;
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] != null) {
                number++;
            }
        }
        return number;
    }

    public void setTheFirstTurn(int turn) {
        theFirstTurn = turn;
    }

    public void resetTable() {
        resetReadyState();
        isPlay = false;
        updatePlayer();
        listJoin.removeAllElements();
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i].resetTimeDb();
                listPlayer[i].parent.updateInventory();
            }
        }
    }

    public void restartTable() {
        isPlay = false;
        for (int i = 0; i < MAX_USERS; i++) {
            listPlayer[i] = null;
        }
        listJoin.removeAllElements();
        viewIndex = maxPlayers;
        maxPlayersInMatch = maxPlayers;
        tableMoneyBet = moneyBetDefault;
        numberReadyPlayres = 0;
        tablePassword = NONE_PASS;
        sendTablePassUpdate();
    }

    public void resetReadyState() {
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i].setReadyState(0);
            }
        }
    }

    public void sendToAll(String msg) {
        for (int i = 0; i < MAX_USERS; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i].response(msg);
            }
        }
    }

    public void sendToPlayer(String msg) {
        for (int i = 0; i < maxPlayers; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i].response(msg);
            }
        }
    }

    public void sendToView(String msg) {
        for (int i = maxPlayers; i < viewIndex; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i].response(msg);
            }
        }
    }

    public void sendReadyState() {
        String msg = "RE";
        for (int i = 0; i < maxPlayers; i++) {
            msg += "|" + ((listPlayer[i] != null) ? listPlayer[i].getReadyState() : 0);
        }
        sendToAll(msg);
    }

    public void sendListPlayer() {
        String msg = "RO";
        msg += "|" + tableMoneyBet;
        for (int i = 0; i < maxPlayers; i++) {
            msg += "|" + ((listPlayer[i] != null) ? listPlayer[i].getUserName() : "~");
        }
        msg += "|" + tableHost;
        msg += "|" + theFirstTurn;

        sendToAll(msg);
    }

    public void sendMoneyInfo() {
        String msg = "MO";
        for (int i = 0; i < maxPlayers; i++) {
            msg += "|" + ((listPlayer[i] != null) ? listPlayer[i].getMoney() : 0);
        }
        sendToAll(msg);
    }

    public void sendUserInfo() {
        // INFO|id|username|sex|exp|money|avatarId
        StringBuilder msg = new StringBuilder();
        msg.append("INFO");
        for (int i = 0; i < MAX_USERS; i++) {
            if (listPlayer[i] != null) {
                msg.append("|").append(listPlayer[i].getPlayerId()).append("|").append(listPlayer[i].getUserName()).append("|").append(listPlayer[i].getGender()).append("|").append(listPlayer[i].getUserLevel()).append("|").append(listPlayer[i].getMoney()).append("|").append(listPlayer[i].getAvatarId());
            }
        }
        sendToAll(msg.toString());
    }

    public synchronized boolean isAllReady() {
        numberReadyPlayres = 0;
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if ((listPlayer[i] != null) && (listPlayer[i].getReadyState() == 1)) {
                numberReadyPlayres++;
            }
        }
        if ((numberReadyPlayres == getNumberActivedPlayers()) && (getNumberActivedPlayers() > 1)) {
            startPlay();
            isPlay = true;
            return true;
        }
        return false;
    }

    public void updateItemForPlayers() {
        for (int i = 0; i < maxPlayersInMatch; i++) {
            if (listPlayer[i] != null) {
                listPlayer[i].parent.updateInventory();
            }
        }
    }

    public void exitTable(int playerId) {
        ClientManager client = listPlayer[playerId].parent;
        client.setEnterTable(false);
        removeUser(playerId);
        gameManager.sendTableInfo(client);
    }

    public synchronized void removeUser(int playerId) {
        if (isPlay && playerId < maxPlayersInMatch) {
            long tempMoney, changeMoney;
            int ratioMoney;
            switch (kindGame) {
                case PHOM:
                    ratioMoney = 4;
                    break;
                case TIEN_LEN:
                    if (getNumberActivedPlayers() == 2) {
                        ratioMoney = 1;
                    } else if (getNumberActivedPlayers() == 3) {
                        ratioMoney = 2;
                    } else if (getNumberActivedPlayers() == 4) {
                        ratioMoney = 3;
                    } else {
                        ratioMoney = 1;
                    }
                    break;
                case BA_CAY:
                    ratioMoney = 1;
                    break;
                case CHESS:
                    ratioMoney = 1;
                    break;
                default:
                    ratioMoney = 1;
                    break;
            }
            tempMoney = listPlayer[playerId].getMoney();
            changeMoney = (tempMoney - ratioMoney * tableMoneyBet >= 0) ? (ratioMoney * tableMoneyBet) : tempMoney;
            tempMoney -= changeMoney;
            listPlayer[playerId].updateMoneyToDatabase(tempMoney);
            StringBuilder msg = new StringBuilder();
            msg.append("CM|").append(playerId).append("|-").append(changeMoney);
            sendToAll(msg.toString());
            sendToAll("FCM|");
        }

        if (playerId == tableHost) {
            for (int i = 0; i < maxPlayers; i++) {
                if (listPlayer[i] != null && i != playerId) {
                    tableHost = i;
                    listPlayer[i].setReadyState(0);
                    break;
                }
            }
        }

        if (theFirstTurn == playerId) {
            for (int i = 0; i < maxPlayers; i++) {
                if (i != playerId && listPlayer[i] != null) {
                    theFirstTurn = i;
                    break;
                }
            }
        }
        if (isPlay) {
            if (playerId < maxPlayers) {
                removePlayer(playerId);
                sendListPlayer();
            } else {
                if (listPlayer[playerId] != null) {
                    removeJoin(listPlayer[playerId].getUserName());
                    removeView(playerId);
                    sendListView();
                }
            }
        } else {
            if (playerId < maxPlayers) {
                removePlayer(playerId);
                sendListPlayer();
            } else {
                removeView(playerId);
                sendListView();
            }
        }
        sendNumberUserInRoomUpdate();

        if (numberUsers == 0) {
            restartTable();
            return;
        }
        if (numberUsers == 1) {
            //loi : co 2 thang, remove 1 thang
            for (int i = 0; i < maxPlayersInMatch; i++) {
                if (listPlayer[i] != null) {
                    listPlayer[i].setReadyState(0);
                }
            }
            isPlay = false;
        }
    }

    public synchronized void removeUser(String name) {
        for (int i = 0; i < listPlayer.length; i++) {
            if (listPlayer[i] != null && listPlayer[i].equals(name)) {
                removeUser(i);
                return;
            }
        }
    }

    public abstract void startPlay();

    public void processMessage(String request, int playerId) {
        String[] message = Utils.split(request, '|');
        if (message.length > 0) {
            if (message[0].equals("RE")) {
                long playerMoney = listPlayer[playerId].getMoney();

                if (playerMoney >= 5 * getBet()) {
                    listPlayer[playerId].setReadyState(Integer.parseInt(message[1]));
                    if (!isAllReady()) {
                        sendReadyState();
                    }
                }
                return;
            }

            if (message[0].equals("RRO")) {
                exitTable(playerId);
                if (getNumberActivedPlayers() == 1) {
                    resetGame();
                }
                return;
            }

            if (message[0].equals("MS")) {
                if (!isPlay) {
                    long moneyBet = Long.parseLong(message[1]);
                    long playerMoney = listPlayer[playerId].getMoney();

                    if (playerMoney < 5 * moneyBet) {
                        return;
                    }
                    
                    if (moneyBet >= moneyBetDefault) {
                        setBet(moneyBet);
                        StringBuilder msg = new StringBuilder();
                        msg.append("MS|").append(message[1]);
                        sendToAll(msg.toString());
                        resetReadyState();
                        sendReadyState();
                    } else {
                        listPlayer[playerId].sendMessage("NMS|" + moneyBetDefault);
                    }
                }
                return;
            }

            if (message[0].equals("GM")) {
                gameType = Integer.parseInt(message[1]);
                for (int i = 0; i < maxPlayers; i++) {
                    if (listPlayer[i] != null) {
                        listPlayer[i].setReadyState(0);
                    }
                }
                sendReadyState();
                sendGameInfo();
                return;
            }

            if (message[0].equals("SU")) {
                int number = Integer.parseInt(message[1]);
                if (number > 1 && number <= maxPlayers) {
                    maxPlayersInMatch = number;
                    sendMaxPlayerInMatchUpdate();
                }
                return;
            }

            if (message[0].equals("PASS")) {
                tablePassword = message[1];
                sendTablePassUpdate();
                return;
            }

            if (message[0].equals("JOIN")) {
                if (isPlay) {
                    if (addJoin(message[1])) {
                        listPlayer[playerId].response("JS|1");
                    } else {
                        listPlayer[playerId].response("JS|0");
                    }
                } else {
                    GamePlayer player = listPlayer[playerId];
                    removeView(playerId);
                    addUser(player);
                    if (player.getPlayerId() >= maxPlayers) {
                        player.response("JS|0");
                    }
                }
                return;
            }

            if (message[0].equals("CHAT")) {
                sendToAll(request + "|" + playerId);
                return;
            }

            if (message[0].equals("KICK")) {
                if (!isPlay) {
                    for (int i = 0; i < maxPlayersInMatch; i++) {
                        if (listPlayer[i] != null && listPlayer[i].getUserName().equals(message[1])) {
                            listPlayer[i].response(request);
                            exitTable(i);
                            break;
                        }
                    }
                }
                return;
            }
            if (message[0].equals("MF")) {
                //MF|idfrom|idto
                userSendId = playerId;
                sendRequestFriend(message[1], message[2], request);
                return;
            }
            if (message[0].equals("RMF")) {
                //RMF|result

                if (Integer.parseInt(message[1]) == 1) {
                    dataEngine.insetNewFriend(listPlayer[userSendId].getUserName(), listPlayer[userReceiveId].getUserName());
                }
                listPlayer[userSendId].response(request);
                return;
            }

            if (message[0].equals("UINFO")) {
                UserInfo userInfo = (message.length == 1 ? listPlayer[playerId].parent.getUserInfo() : listPlayer[playerId].parent.getUserInfo(message[1]));
                sendUserInfo(listPlayer[playerId].parent, userInfo);
                return;
            }

            if (message[0].equals("MSG")) {
                gameManager.sendMessage(listPlayer[playerId].parent, message[1], Integer.parseInt(message[2]), message[3]);
                return;
            }

            if (message[0].equals("SPEAK")) {
                if (listPlayer[playerId].parent.hasSpeaker()) {
                    listPlayer[playerId].parent.deleteSpeaker();
                    String msg = "SPEAK";
                    msg += "|" + listPlayer[playerId].getUserName();
                    msg += "|" + message[1];
                    parent.sendToAllPlayerInGame(msg, listPlayer[playerId].parent.getGameType());
                } else {
                    listPlayer[playerId].parent.sendMessage("SPEAK|0");
                }
                return;
            }

            if (message[0].equals("CHECKSMS")) {
                //reply 1: thanh cong , 0: nhan qua tin trong 5 phut, 2: nhan qua 150 000 
                listPlayer[playerId].parent.checkSms();
                return;
            }
            if (message[0].equals("SMSINFO")) {
//                String msg = "SMSINFO";
//                for (int i = 0; i < listPlayer[playerId].parent.getSmsInfo().size(); i++) {
//                    msg += "|" + listPlayer[playerId].parent.getSmsInfo().get(i) + "|" + GameManager.SMS_SYNTAX;
//                }
                GamePlayer player = listPlayer[playerId];
                player.parent.sendSmsInfo(player.getCompanyId());
                return;
            }
            if (message[0].equals("SETMONEY")) {
                listPlayer[playerId].parent.setMoney(Long.parseLong(message[1]));
                return;
            }
            if (message[0].equals("USECARD")) {
                //USECARD|issuer|cardCode|cardSerial
                listPlayer[playerId].parent.useCard(message[1], message[2], message[3]);

                return;
            }
        }
    }

    public synchronized boolean addUser(GamePlayer player) {
        player.setReadyState(0);
        if (numberUsers < MAX_USERS) {
            if (numberUsers == 0) {
                tableHost = 0;
                theFirstTurn = tableHost;

            }
            boolean result;
            if (isPlay) {
                result = addView(player);
            } else {
                if ((numberUsers - viewIndex + maxPlayers) < maxPlayersInMatch) {
                    result = addPlayer(player);
                } else {
                    result = addView(player);
                }
            }
            if (result) {
                sendTableInfo();
                sendNumberUserInRoomUpdate();
                return true;
            }
        }
        return false;
    }

    private void sendUserInfo(ClientManager client, UserInfo userInfo) {
        //INFO|name|money|gender|level|win|lose|status|levelComplete
        StringBuilder infoBuilder = new StringBuilder();
        infoBuilder.append("UINFO|");
        infoBuilder.append(userInfo.getUserName());
        infoBuilder.append("|");
        infoBuilder.append(userInfo.getMoney());
        infoBuilder.append("|");
        infoBuilder.append(userInfo.getGender());
        infoBuilder.append("|");
        infoBuilder.append(userInfo.getLevel());
        infoBuilder.append("|");
        infoBuilder.append(userInfo.getNumberWin());
        infoBuilder.append("|");
        infoBuilder.append(userInfo.getNumberLose());
        infoBuilder.append("|");
        infoBuilder.append((userInfo.getStatus().equals("") ? "0" : userInfo.getStatus()));
        infoBuilder.append("|");
        infoBuilder.append((userInfo.getAvatarId()));
        infoBuilder.append("|");
        infoBuilder.append(userInfo.getLevelComplete());
        client.sendMessage(infoBuilder.toString());
        MateInfo mateInfo = client.getMateInfo(userInfo.getUserName());
        if (mateInfo == null) {
            client.sendMessage("FAMILY|0");
        } else {
            client.setMateInfo(mateInfo);
            List<ChildInfo> childList = client.getChildInfo(userInfo.getUserName());
            StringBuilder childInfoBuilder = new StringBuilder();
            int numberChild = childList.size();
            childInfoBuilder.append("FAMILY|1|");
            childInfoBuilder.append(mateInfo.getMateName1());
            childInfoBuilder.append("|");
            childInfoBuilder.append(mateInfo.getMateName2());
            childInfoBuilder.append("|");
            childInfoBuilder.append(numberChild);
            for (int i = 0; i < numberChild; i++) {
                childInfoBuilder.append("|");
                childInfoBuilder.append(childList.get(i).getChildName());
                childInfoBuilder.append("|");
                childInfoBuilder.append(childList.get(i).getChildGender());
            }
            client.sendMessage(childInfoBuilder.toString());

        }
    }

    protected abstract void resetGame();

    public abstract void sendGameInfo();
}
